package com.evil.common.oss.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.digest.MD5;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.PasswordUtil;
import com.evil.common.file.constant.OssConstant;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

@Slf4j
public class FileUtil {

    private static final Pattern FILE_PATTERN = Pattern.compile("[\\\\/:*?\"<>|]");

    /**
     * 生成fileCode
     *
     * @param userId userId
     * @return String
     */
    public static String getFileCode(Long userId) {
        if (null == userId) {
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, "fileCode生成失败");
        }
        String uuid = UUID.randomUUID().toString(true);
        long current = DateUtil.current(true);


        String substring = Objects.requireNonNull(PasswordUtil.getSecretPassword(userId.toString(), Long.toString(current))).substring(0, 6);
        return uuid + substring;
    }

    /**
     * 生成文件签名
     *
     * @param fileCode  文件code
     * @param timestamp 过期时间
     * @param token     登录人token
     * @return String signature
     */
    public static String generateSignature(@NotBlank(message = "文件code不能为空") String fileCode,
                                           @NotNull(message = "过期时间不能为空") Long timestamp,
                                           @NotBlank(message = "token不能为空") String token) {
        String data = String.format("%s%s%s%s", fileCode, timestamp, token, OssConstant.FILE_PRIVATE_KEY);
        return MD5.create().digestHex(data);
    }

    /**
     * 生成文件下载请求URL
     *
     * @param host     host
     * @param fileCode 文件code
     * @param token    登录人token
     * @return String
     */
    public static String generateFileUrl(String host, String fileCode, String token) {
        Map<String, Object> params = FileUtil.generateFileParam(fileCode, token);
        // 如果需要限制下载次数，试用redis限制，那么在这里处理存redis
        return String.format("%s?%s", host, URLUtil.buildQuery(params, StandardCharsets.UTF_8));
    }

    /**
     * 文件验签
     *
     * @param fileCode  文件code
     * @param timestamp 过期时间
     * @param signature 签名
     */
    public static void checkFileSignature(String fileCode, Long timestamp, String token, String signature) {
        if (!FileUtil.verifyFileSignature(fileCode, timestamp, token, signature)) {
            throw new BusinessException(RCodeEnum.FILE_SIGNATURE_ERROR);
        }
    }

    /**
     * 文件验签
     *
     * @param fileCode  文件code
     * @param timestamp 过期时间
     * @param signature 签名
     * @return boolean
     */
    public static boolean verifyFileSignature(String fileCode, Long timestamp, String token, String signature) {
        // 如果需要限制下载次数，试用redis限制，那么在这里处理校验redis
        if (new Date().getTime() > timestamp) {
            return false;
        }
        return FileUtil.generateSignature(fileCode, timestamp, token).equals(signature);
    }

    /**
     * 生成文件下载URL 参数
     *
     * @param fileCode 文件code
     * @param token    登录人token
     * @return String
     */
    private static Map<String, Object> generateFileParam(String fileCode, String token) {
        // 过期时间
        long timestamp = DateUtil.offsetMinute(new Date(), OssConstant.FILE_SIGNATURE_EXPIRATION_TIME).getTime();
        // 签名
        String signature = FileUtil.generateSignature(fileCode, timestamp, token);

        Map<String, Object> params = new HashMap<>();
        params.put("fileCode", fileCode);
        params.put("timestamp", timestamp);
        params.put("token", token);
        params.put("signature", signature);
        return params;
    }

    /**
     * 写文件
     *
     * @param filePath 文件保存路径
     * @param is       源文件
     * @return 文件路径
     */
    public static String writeFile(String filePath, String fileName, InputStream is) throws Exception {
        // 检查文件夹
        FileUtil.checkDir(filePath);
        String path = filePath + "/" + fileName;
        FileOutputStream outputStream = new FileOutputStream(path);
        int index;
        byte[] b = new byte[1024];
        while ((index = is.read(b)) != -1) {
            outputStream.write(b, 0, index);
            outputStream.flush();
        }
        is.close();
        outputStream.close();
        return path;
    }

    /**
     * 检查文件夹
     *
     * @param path path
     */
    public static void checkDir(String path) {
        // 判断路径是否存在不存在就创建
        String[] paths = path.split("/");
        String dir = paths[0];
        for (int i = 0; i < paths.length - 1; i++) {
            dir = dir + "/" + paths[i + 1];
            File file = new File(dir);
            if (!file.exists()) {
                if (!file.mkdir()) {
                    log.debug("文件路径创建失败" + file.getPath());
                }
            }
        }
    }

    /**
     * 文件名特殊字符过滤
     *
     * @param name    name
     * @param replace replace
     * @return String
     */
    public static String filenameFilter(String name, String replace) {
        return name == null ? null : FILE_PATTERN.matcher(name).replaceAll(replace).replaceFirst(" ", "");
    }

    /**
     * 文件名特殊字符过滤
     *
     * @param name name
     * @return String
     */
    public static String filenameFilter(String name) {
        return filenameFilter(name, "");
    }
}
